#include <utility>

// Windows 8+ does not like abort() in Release mode
#ifdef _WIN32
#define ABORT_NO_BACKTRACE() _exit(134)
#else
#define ABORT_NO_BACKTRACE() abort()
#endif

struct AssertionInfo {
  const char* file_line; // filename:line
  const char* message;
  const char* function;
};

inline void Abort() {
  //  DumpBacktrace(stderr);
  fflush(stderr);
  ABORT_NO_BACKTRACE();
}

inline void Assert(const AssertionInfo& info) {
  //  std::string name = GetHumanReadableProcessName();

  fprintf(stderr, "%s:%s%s Assertion `%s' failed.\n", info.file_line, info.function, *info.function ? ":" : "", info.message);
  fflush(stderr);

  Abort();
}

// Macros stolen from Node
#define ERROR_AND_ABORT(expr)                                                                                                              \
  do {                                                                                                                                     \
    /* Make sure that this struct does not end up in inline code, but      */                                                              \
    /* rather in a read-only data section when modifying this code.        */                                                              \
    static const AssertionInfo args = {__FILE__ ":" STRINGIFY(__LINE__), #expr, PRETTY_FUNCTION_NAME};                                     \
    Assert(args);                                                                                                                          \
  } while (0)

#ifdef __GNUC__
#define LIKELY(expr) __builtin_expect(!!(expr), 1)
#define UNLIKELY(expr) __builtin_expect(!!(expr), 0)
#define PRETTY_FUNCTION_NAME __PRETTY_FUNCTION__
#else
#define LIKELY(expr) expr
#define UNLIKELY(expr) expr
#define PRETTY_FUNCTION_NAME ""
#endif

#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)

#define CHECK(expr)                                                                                                                        \
  do {                                                                                                                                     \
    if (UNLIKELY(!(expr))) {                                                                                                               \
      ERROR_AND_ABORT(expr);                                                                                                               \
    }                                                                                                                                      \
  } while (0)

#define CHECK_EQ(a, b) CHECK((a) == (b))
#define CHECK_GE(a, b) CHECK((a) >= (b))
#define CHECK_GT(a, b) CHECK((a) > (b))
#define CHECK_LE(a, b) CHECK((a) <= (b))
#define CHECK_LT(a, b) CHECK((a) < (b))
#define CHECK_NE(a, b) CHECK((a) != (b))
#define CHECK_NULL(val) CHECK((val) == nullptr)
#define CHECK_NOT_NULL(val) CHECK((val) != nullptr)
#define CHECK_IMPLIES(a, b) CHECK(!(a) || (b))
